from pydantic import BaseModel, Field, validator
from typing import Optional, List, Dict, Any, Union, Generic, TypeVar
from datetime import datetime
from enum import Enum

T = TypeVar('T')

class APIResponse(BaseModel, Generic[T]):
    """统一API响应格式"""
    success: bool = True
    message: str = "操作成功"
    data: Optional[T] = None
    error_code: Optional[str] = None
    timestamp: datetime = Field(default_factory=datetime.now)

class TaskStatus(str, Enum):
    PENDING = "pending"
    RUNNING = "running"
    SUCCESS = "success"
    FAILED = "failed"
    CANCELLED = "cancelled"
    PAUSED = "paused"

class CrawlStrategy(str, Enum):
    BASIC = "basic"
    DEEP = "deep"
    SMART = "smart"
    CUSTOM = "custom"

class TaskConfig(BaseModel):
    """任务配置模型"""
    name: str = Field(..., max_length=100, description="任务名称")
    description: Optional[str] = Field(None, max_length=500, description="任务描述")
    urls: List[str] = Field(..., min_items=1, description="目标URL列表")
    strategy: CrawlStrategy = Field(CrawlStrategy.BASIC, description="爬取策略")
    max_depth: int = Field(3, ge=1, le=10, description="最大爬取深度")
    delay_min: float = Field(1.0, ge=0.1, le=10.0, description="最小请求间隔(秒)")
    delay_max: float = Field(3.0, ge=0.1, le=10.0, description="最大请求间隔(秒)")
    concurrent_requests: int = Field(5, ge=1, le=20, description="并发请求数")
    timeout: int = Field(30, ge=5, le=300, description="请求超时时间(秒)")
    retry_attempts: int = Field(3, ge=0, le=10, description="重试次数")
    extraction_rules: Dict[str, str] = Field(default_factory=dict, description="数据提取规则")
    headers: Dict[str, str] = Field(default_factory=dict, description="自定义请求头")
    cookies: Dict[str, str] = Field(default_factory=dict, description="自定义Cookie")
    proxy_config: Optional[Dict[str, Any]] = Field(None, description="代理配置")
    schedule_cron: Optional[str] = Field(None, description="定时调度表达式")
    
    @validator('delay_max')
    def validate_delay_range(cls, v, values):
        if 'delay_min' in values and v < values['delay_min']:
            raise ValueError('delay_max must be greater than or equal to delay_min')
        return v
    
    @validator('urls')
    def validate_urls(cls, v):
        import re
        url_pattern = re.compile(
            r'^https?://'  # http:// or https://
            r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?|'  # domain...
            r'localhost|'  # localhost...
            r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'  # ...or ip
            r'(?::\d+)?'  # optional port
            r'(?:/?|[/?]\S+)$', re.IGNORECASE)
        
        for url in v:
            if not url_pattern.match(url):
                raise ValueError(f'Invalid URL format: {url}')
        return v

class TaskInfo(BaseModel):
    """任务信息模型"""
    id: str
    config: TaskConfig
    status: TaskStatus
    created_at: datetime
    updated_at: datetime
    started_at: Optional[datetime] = None
    completed_at: Optional[datetime] = None
    progress: float = Field(0.0, ge=0.0, le=100.0)
    pages_crawled: int = Field(0, ge=0)
    data_extracted: int = Field(0, ge=0)
    errors_count: int = Field(0, ge=0)
    success_rate: float = Field(0.0, ge=0.0, le=100.0)
    user_id: str

class TaskCreateRequest(BaseModel):
    """创建任务请求"""
    config: TaskConfig
    execute_immediately: bool = Field(False, description="是否立即执行")

class TaskUpdateRequest(BaseModel):
    """更新任务请求"""
    config: TaskConfig
    restart_if_running: bool = Field(False, description="如果正在运行是否重启")

class PaginationParams(BaseModel):
    """分页参数"""
    page: int = Field(1, ge=1, description="页码")
    size: int = Field(20, ge=1, le=100, description="每页数量")

class TaskListParams(PaginationParams):
    """任务列表查询参数"""
    status: Optional[TaskStatus] = Field(None, description="状态过滤")
    search: Optional[str] = Field(None, description="关键词搜索")
    created_after: Optional[datetime] = Field(None, description="创建时间过滤(开始)")
    created_before: Optional[datetime] = Field(None, description="创建时间过滤(结束)")

class PaginatedResponse(BaseModel):
    """分页响应"""
    items: List[Any]
    total: int
    page: int
    size: int
    pages: int
    
    @validator('pages', always=True)
    def calculate_pages(cls, v, values):
        if 'total' in values and 'size' in values:
            return (values['total'] + values['size'] - 1) // values['size']
        return v

class LogLevel(str, Enum):
    DEBUG = "debug"
    INFO = "info"
    WARNING = "warning"
    ERROR = "error"
    CRITICAL = "critical"

class LogEntry(BaseModel):
    """日志条目"""
    id: str = Field(..., description="日志ID")
    task_id: str = Field(..., description="任务ID")
    execution_id: Optional[str] = Field(None, description="执行ID")
    level: LogLevel = Field(..., description="日志级别")
    message: str = Field(..., description="日志消息")
    timestamp: datetime = Field(..., description="时间戳")
    url: Optional[str] = Field(None, description="相关URL")
    extra_data: Optional[Dict[str, Any]] = Field(None, description="额外数据")

class CrawlResult(BaseModel):
    """爬取结果"""
    id: str = Field(..., description="结果ID")
    task_id: str = Field(..., description="任务ID")
    execution_id: str = Field(..., description="执行ID")
    url: str = Field(..., description="源URL")
    title: Optional[str] = Field(None, description="页面标题")
    content: Optional[str] = Field(None, description="页面内容")
    extracted_data: Dict[str, Any] = Field(default_factory=dict, description="提取的结构化数据")
    metadata: Dict[str, Any] = Field(default_factory=dict, description="元数据")
    crawled_at: datetime = Field(..., description="爬取时间")
    response_time: float = Field(0.0, description="响应时间(秒)")
    status_code: int = Field(200, description="HTTP状态码")

# User related schemas
class UserBase(BaseModel):
    username: str = Field(..., min_length=3, max_length=50)
    email: str = Field(..., pattern=r'^[^@]+@[^@]+\.[^@]+$')
    full_name: Optional[str] = Field(None, max_length=100)

class UserCreate(UserBase):
    password: str = Field(..., min_length=6)

class UserLogin(BaseModel):
    username: str
    password: str

class UserInfo(UserBase):
    id: str
    role: str
    is_active: bool
    created_at: datetime
    last_login: Optional[datetime] = None

class Token(BaseModel):
    access_token: str
    refresh_token: str
    token_type: str = "bearer"
    expires_in: int
    user_info: UserInfo
